﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using TallComponents.Web.Pdf;
using TallComponents.Web.Pdf.Resources;

namespace TallComponents.Web.Storage
{
   /// <summary>
   /// Storage provider that uses session state to store PDF documents.
   /// </summary>
   public class SessionStorageProvider : PdfStorageProvider
   {
      private static readonly string storageKey = typeof(SessionStorageProvider).FullName;

      /// <summary>
      /// Initializes a new instance of the <see cref="SessionStorageProvider"/> class.
      /// </summary>
      public SessionStorageProvider()
      {

      }

      /// <summary>
      /// Stores a file.
      /// </summary>
      /// <param name="fileName">Name of the file.</param>
      /// <param name="data">A seekable stream containing the file data.</param>
      /// <returns>The unique key for the file.</returns>
      /// <exception cref="ArgumentNullException">Occures when <paramref name="fileName"/> or <paramref name="data"/> is <c>null</c>.</exception>
      /// <remarks>The <paramref name="data"/> stream may be closed immediatly after this method returns.</remarks>
      public override string StoreFile(string fileName, Stream data)
      {
         if (null == data)
            throw new ArgumentNullException("data");

         if (string.IsNullOrEmpty(fileName))
            throw new ArgumentNullException(Pdf.Resources.Text.PdfStorageProvider_Empty_FileName_Not_Allowed, "fileName");

         CachedFile cache;
         try
         {
            cache = new CachedFile(Guid.NewGuid().ToString(), fileName, data);
         }
         catch (Exception ex)
         {
            throw new ArgumentException(Pdf.Resources.Text.PdfStorageProvider_Invalid_Document, "data", ex);
         }

         if (null != Context.Session[storageKey])
         {
            Context.Session.Remove(storageKey); // remove previous file form session storage
         }
         Context.Session.Add(storageKey, cache);

         return cache.StorageKey;
      }

      /// <summary>
      /// Gets information about a PDF document.
      /// </summary>
      /// <param name="key">The storage key.</param>
      /// <returns>
      /// A <see cref="TallComponents.Web.Pdf.Document"/> object describing the document with the specified <paramref name="key"/> or <c>null</c> if the key is not valid or associated with an invalid document.
      /// </returns>
      public override Document GetDocument(string key)
      {
         var cache = Context.Session[storageKey] as CachedFile;
         if (null != cache)
         {
            if (cache.StorageKey.Equals(key, StringComparison.OrdinalIgnoreCase))
            {
               return cache.Document;
            }
         }
         return null;
      }


      /// <summary>
      /// Gets information on all the documents in storage.
      /// </summary>
      /// <returns>
      /// A list of <see cref="Document"/> objects, describing all PDF documents managed by this provider.
      /// </returns>
      public override IList<Document> GetDocuments()
      {
         var result = new List<Document>();
         var cache = Context.Session[storageKey] as CachedFile;
         if (null != cache)
         {
            result.Add(cache.Document);
         }

         return result;
      }

      [Serializable]
      private class CachedFile
      {
         public CachedFile(string key, string fileName, Stream data)
         {
            FileName = fileName;
            StorageKey = key;
            _data = new byte[data.Length];
            if (data.CanSeek)
            {
               data.Seek(0, SeekOrigin.Begin);
            }
            data.Read(_data, 0, (int)data.Length);
            try
            {
               _document = new Document(new MemoryStream(_data), FileName, StorageKey);
            }
            catch (Exception ex)
            {
               throw new ArgumentException(Pdf.Resources.Text.PdfStorageProvider_Invalid_Document, "data", ex);
            }
         }

         public string FileName { get; private set; }
         public string StorageKey { get; private set; }
         public Document Document
         {
            get
            {
               if (null == _document)
               {
                  _document = new Document(new MemoryStream(_data), FileName, StorageKey);
               }
               return _document;
            }
         }

         private byte[] _data;
         [NonSerialized]
         private Document _document;

      }

#if NET35 || NET40
      /// <summary>
      /// Gets the http context.
      /// </summary>
      /// <value>The context.</value>
      protected virtual HttpContextBase Context
      {
         get
         {
            if ( null == HttpContext.Current )
               throw new InvalidOperationException( Text.SessionStorageProvider_not_availale_in_this_context );

            return new HttpContextWrapper( HttpContext.Current );
         }
      }
#else
      private HttpContext Context
      {
         get
         {
            if (null == HttpContext.Current)
               throw new InvalidOperationException(Text.SessionStorageProvider_not_availale_in_this_context);

            return HttpContext.Current;
         }
      }
#endif

   }
}
